/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using SuperListEx.ItemLists;
using SuperListEx.Sections;
using SuperListEx.WinFormsUtility.Controls;
using SuperListEx.WinFormsUtility.Drawing;
using System.Collections.Generic;


namespace SuperListEx
{
	public class ListControl : SectionContainerControl
    {
        #region vars
        private int test = 0;
        private Section _customiseListSection;
        private bool _allowSorting = true;
        List<Keys> _interestedKeys = new List<Keys>() { Keys.Space, Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Multiply, Keys.Subtract };
        #endregion

        #region Events
        public event EventHandler CalculatedGroupRows;
        public event EventHandler<RowEventArgs> MouseClickedRow;
        public event EventHandler<CellEventArgs> MouseClickedCell;
        public event EventHandler<RowFocusChangedEventArgs> RowFocusChanged;
        public event EventHandler RowDoubleClicked;
        #endregion        

        #region Delegates
        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public CustomColorDelegate CustomBackgroundColorAccessor { get; set;}

        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public CustomColorDelegate CustomForegroundColorAccessor{get; set;}

        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public CustomFontDelegate CustomFontAccessor { get; set; }
        #endregion

        #region Properties
        public bool MultiSelect
        {
            get { return ListSection.AllowMultiSelect; }
            set { ListSection.AllowMultiSelect = value; }
        }

        public bool ShowHeaderSection
        {
            get { return ListSection.ShowHeaderSection; }
            set { ListSection.ShowHeaderSection = value; }
        }

        public ListSection ListSection
		{
            get;
            private set;
		}	

        public ColumnList Columns
		{
            get;
            private set;
		}

        public ItemList Items
        {
            get;
            private set;
        }

		public SelectedItemsCollection SelectedItems
		{   
            get;
            private set;
		}

        public Color SeparatorColor
		{
            get;
            set;
		}

		public Color IndentColor
		{
            get;
            set;
		}

        public VerticalAlignment GroupSectionVerticalAlignment
		{
            get;
            set;
		}

        public Color AlternatingRowColor
		{
            get;
            set;
		}

        public Font GroupSectionFont
        {
            get;
            set;
        }

        public Color GroupSectionForeColor
        {
            get;
            set;
        }

        public string GroupSectionTextSingular
        {
            get;
            set;
        }

        public string GroupSectionTextPlural
        {
            get;
            set;
        }

        public int ItemCount
        {
            get
            {
                return Items.Count;
            }
        }

        public int SelectedItemCount
        {
            get
            {
                return GetSelectedItemList<object>().Count;
            }
        }

        public List<T> GetItemList<T>()
        {
            List<T> toReturn = new List<T>();
            Items.ToList().ForEach(I => toReturn.Add((T)I));
            return toReturn;
        }

        public List<T> GetSelectedItemList<T>()
        {
            List<T> toReturn = new List<T>();

            foreach (RowIdentifier ri in SelectedItems)
            {
                foreach (object o in ri.Items)
                    toReturn.Add((T)o);
            }

            return toReturn;
        }

        public bool AllowSorting
        {
            get
            {
                return _allowSorting;
            }
            set
            {
                if (_allowSorting != value)
                {
                    _allowSorting = value;
                    foreach (Column clm in Columns)
                    {
                        clm.ShowHeaderSortArrow = _allowSorting;
                    }
                    LayoutControl();
                }
            }
        }

        public object FocusedItem
        {
            get
            {
                return ListSection.FocusedItem == null ? null : ListSection.FocusedItem.RowIdentifier.Items[0];
            }
            set
            {
                if (value != null)
                {
                    PositionedRowIdentifier si = ListSection.GetPositionedIdentifierFromObject(new NonGroupRow(value));

                    if (si != null)
                    {
                        int pos = si.Position - 1;

                        while (pos >= 0)
                        {
                            RowIdentifier gi = ListSection.RowInformation[pos];

                            if (gi is GroupIdentifier)
                            {
                                ListSection.FocusedItem = new PositionedRowIdentifier(gi, pos);

                                --pos;
                            }
                            else
                            {
                                break;
                            }
                        }

                        ListSection.FocusedItem = si;
                    }
                }
                else
                {
                    ListSection.FocusedItem = null;
                }
            }
        }

        public bool ShowCustomizeSection
        {
            get
            {
                return _customiseListSection != null;
            }
            set
            {
                if (ShowCustomizeSection != value)
                {
                    if (_customiseListSection == null)
                    {
                        _customiseListSection = SectionFactory.CreateCustomiseListSection(this);
                        Canvas.Children.Insert(0, _customiseListSection);
                    }
                    else
                    {
                        Canvas.Children.Remove(_customiseListSection);
                        _customiseListSection.Dispose();
                        _customiseListSection = null;
                    }
                    Canvas.Host.LazyLayout(Canvas);
                }
            }
        }

        public string SelectedItemsInTabbedFormat
        {
            get
            {
                bool First = true;
                StringBuilder ReturnString = new StringBuilder();
                List<object> items = GetItemList<object>();

                foreach (object o in items)
                {
                    if (!First)
                    {
                        ReturnString.Append("\n");
                    }

                    foreach (Column C in Columns)
                    {
                        if (!(o is Bitmap))
                        {
                            if (C.ColumnItemAccessor != null)
                            {
                                ReturnString.Append(C.ColumnItemAccessor.Invoke(o).ToString() + "\t");
                            }

                            else if (C.ColumnItemAccessorWithParameter != null)
                            {
                                ReturnString.Append(C.ColumnItemAccessorWithParameter.Invoke(o, C.AccessorParameter).ToString() + "\t");
                            }
                        }

                    }
                    First = false;
                }


                foreach (RowIdentifier RI in SelectedItems)
                {
                    if (!First)
                    {
                        ReturnString.Append("\n");
                    }

                    foreach (Column C in Columns.VisibleItems)
                    {
                        if (C.ColumnItemAccessor != null)
                        {
                            if (C.ColumnItemAccessor.Invoke(RI.Items[0]).GetType() != typeof(Bitmap))
                            {
                                ReturnString.Append(C.ColumnItemAccessor.Invoke(RI.Items[0]).ToString() + "\t");
                            }
                        }

                        else if (C.ColumnItemAccessorWithParameter != null)
                        {
                            if (C.ColumnItemAccessorWithParameter.Invoke(RI.Items[0], C.AccessorParameter).GetType() != typeof(Bitmap))
                            {
                                ReturnString.Append(C.ColumnItemAccessor.Invoke(RI.Items[0]).ToString() + "\t");
                            }
                        }

                    }

                    First = false;
                }

                return (ReturnString.ToString());
            }
        }

        public override SectionFactory SectionFactory
        {
            set
            {
                base.SectionFactory = value;

                Canvas.Children.Remove(_customiseListSection);
                Canvas.Children.Remove(ListSection);
                if (_customiseListSection != null)
                {
                    _customiseListSection.Dispose();
                }
                bool headerShown = ListSection.ShowHeaderSection;
                ListSection.Dispose();

                if (ShowCustomizeSection)
                {
                    _customiseListSection = SectionFactory.CreateCustomiseListSection(this);
                    Canvas.Children.Add(_customiseListSection);
                }

                ListSection = SectionFactory.CreateListSection(this);
                ListSection.ShowHeaderSection = headerShown;
                Canvas.Children.Add(ListSection);
            }
        }

        public bool AllowRowDragDrop
        {
            get { return ListSection.AllowRowDragDrop; }
            set { ListSection.AllowRowDragDrop = value; }
        }
        #endregion

        #region Constuctors
        public ListControl( SectionFactory sectionFactory )
			: base( sectionFactory )
		{
            Columns = new ColumnList();
            Items = new BetterItemList();
            SelectedItems = new SelectedItemsCollection(this);
            KeyDown += new System.Windows.Forms.KeyEventHandler(ListControl_KeyDown);

            Name = "ListControl";
            GroupSectionTextSingular = "Item";
            GroupSectionTextPlural = "Items";

            GroupSectionVerticalAlignment = VerticalAlignment.Top;
            AlternatingRowColor = Color.Empty;
            GroupSectionForeColor = SystemColors.WindowText;
            Items.ListControl = this;
            _customiseListSection = SectionFactory.CreateCustomiseListSection(this);
            ListSection = SectionFactory.CreateListSection(this);
            Canvas.Children.Add(_customiseListSection);
            Canvas.Children.Add(ListSection);
            this.CustomFontAccessor = delegate(object o) { return SystemFonts.DialogFont; };
            this.CustomBackgroundColorAccessor = delegate(object o) { return this.BackColor; };
            this.CustomForegroundColorAccessor = delegate(object o) { return this.ForeColor; };
		}

		public ListControl()
			: this( new SectionFactory() )
		{
            Columns.Parent = this;
        }
        #endregion

        #region Serilization
        public string SerializeState()
        {
            MemoryStream ms = new MemoryStream();
            StreamWriter writer = new StreamWriter(ms);
            writer.AutoFlush = true;
            SerializeState(writer);

            StreamReader reader = new StreamReader(new MemoryStream(ms.GetBuffer()));
            return reader.ReadToEnd();
        }

        public bool LoadState(string s)
        {
            try
            {
                if (s != "")
                {
                    MemoryStream ms = new MemoryStream();
                    StreamWriter writer = new StreamWriter(ms);
                    writer.WriteLine(s);
                    ms.Seek(0, SeekOrigin.Begin);
                    this.DeSerializeState(new StreamReader(ms));                    
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        public virtual void SerializeState( TextWriter writer )
		{
			ListSection.SerializeState( writer );
		}

		public virtual void DeSerializeState( TextReader reader )
		{
			ListSection.DeSerializeState( reader );
        }
        #endregion

        #region Layout
        public void LayoutSections()
		{
			Canvas.Host.LazyLayout( Canvas );
		}

		/// <summary>
		/// Sets the column width to fit the specified columns contents.
		/// </summary>
		/// <param name="columns">Columns to size</param>
		public void SizeColumnsToFit(IEnumerable<Column> columns )
		{
            List<Column> c = new List<Column>(columns);
			ListSection.SizeColumnsToFit( c.ToArray() );
        }
        #endregion

        #region Mouse
        protected virtual void OnMouseClickedRow(RowEventArgs eventArgs)
        {
            if (MouseClickedRow != null)
            {
                MouseClickedRow(this, eventArgs);
            }
        }

        protected virtual void OnRowDoubleClicked(RowSection rowSection)
        {
            if (RowDoubleClicked != null)
            {
                RowDoubleClicked(rowSection, EventArgs.Empty);
            }
        }

        protected virtual void OnMouseClickedCell(CellEventArgs eventArgs)
        {
            if (MouseClickedCell != null)
            {
                MouseClickedCell(this, eventArgs);
            }
        }

        public void PerformMouseWheel(int delta)
        {
            if (ListSection != null)
            {
                ListSection.MouseWheel(new MouseEventArgs(MouseButtons.None, 0, 0, 0, delta));
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            if (ListSection != null)
            {
                ListSection.MouseWheel(e);
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);

            Section section = SectionFromPoint(e.Location);
            while (section != null && !(section is RowSection))
            {
                section = section.Parent;
            }

            if (section != null)
            {
                RowSection rs = section as RowSection;
                OnMouseClickedRow(new RowEventArgs(rs.RowIdentifier));

                CellSection cs = rs.CellSectionFromPoint(new Point(e.X, e.Y));
                if (cs != null)
                {
                    OnMouseClickedCell(new CellEventArgs(cs));
                }
            }
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);
            if (RowDoubleClicked != null)
            {
                Section section = SectionFromPoint(PointToClient(MousePosition));
                while (section != null)
                {
                    if (section is RowSection)
                    {
                        OnRowDoubleClicked((RowSection)section);
                        break;
                    }
                    section = section.Parent;
                }
            }
        }
        #endregion

        #region Keyboard
        protected override bool IsInputKey(Keys keyData)
        {
            return _interestedKeys.Contains(keyData);
        }

        private void ListControl_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control)
            {
                switch (e.KeyCode)
                {
                    case Keys.C:
                        if (SelectedItems.Count > 0)
                        {
                            StringBuilder strBldr = new StringBuilder();
                            foreach (RowIdentifier row in SelectedItems)
                            {
                                if (row.Items.Count > 0)
                                {
                                    strBldr.AppendLine(row.Items[0].ToString());
                                }
                            }
                            Clipboard.SetDataObject(strBldr.ToString());
                        }
                        break;
                    case Keys.A:
                        SelectedItems.Clear();
                        SelectedItems.AddRange(Items.ToList());
                        break;
                }
            }
        }
        #endregion

        #region Control Events
        protected virtual void OnRowFocusChanged(RowFocusChangedEventArgs eventArgs)
        {
            if (RowFocusChanged != null)
            {
                RowFocusChanged(this, eventArgs);
            }
        }

        protected internal virtual void OnCalculatedGroupRows(EventArgs e)
        {
            if (CalculatedGroupRows != null)
            {
                CalculatedGroupRows(this, e);
            }
        }
        #endregion

        #region Public functions
        public void EnsureVisible(object o)
        {
            if (o != null)
            {
                PositionedRowIdentifier si = ListSection.GetPositionedIdentifierFromObject(new NonGroupRow(o));

                if (si != null)
                {
                    int pos = si.Position - 1;

                    while (pos >= 0)
                    {
                        RowIdentifier gi = ListSection.RowInformation[pos];

                        if (gi is GroupIdentifier)
                        {
                            ListSection.FocusedItem = new PositionedRowIdentifier(gi, pos);
                            --pos;
                        }
                        else
                        {
                            break;
                        }
                    }

                    ListSection.FocusedItem = si;
                }
            }
            else
            {
                ListSection.FocusedItem = null;
            }
        }

        public void SetGlobalGroupState(GroupState state)
        {
            ListSection.SetGlobalGroupState(state);
        }
        #endregion

		internal void UpdateListSection( bool lazyLayout )
		{
            ListSection.ListUpdated(lazyLayout);
		}

		internal void FireFocusChanged( RowIdentifier oldFocusItem, RowIdentifier newFocusItem )
		{
			OnRowFocusChanged( new RowFocusChangedEventArgs( oldFocusItem, newFocusItem ) );
		}        
	}
}